﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Text;
using IsmsExplorer.Infrastructure.DomainBase;
using IsmsExplorer.Infrastructure.RepositoryFramework.Configuration;

namespace IsmsExplorer.Infrastructure.RepositoryFramework {
    public static class RepositoryFactory {
        // Repositories are cached here (singleton effect) to be reused to avoid recreating.
        // NOTE: we create two types of repositories to cater for null UnitOfWork implementations (e.g. when not needed to 
        //       commit information back to database, UnitOfWork can be null).
        //       This is because if only one repository was used, if we created a Repository with null UnitOfWork, there
        //       is currently no way to assign/check the Repository's UnitOfWork as it's not part of the IRepository<T> 
        //       interface. May change the implementation in the future, in which case this code could be revisited.
        static Dictionary<string, object> _repositories = new Dictionary<string, object>();
        static Dictionary<string, object> _repositoriesNullUnitOfWork = new Dictionary<string, object>();

        public static TRepository GetRepository<TRepository, TEntity>(IUnitOfWork unitOfWork)
            where TRepository : class, IRepository<TEntity>
            where TEntity : EntityBase 
        {            
            TRepository repository = default(TRepository);
            // Choose which cache we're using, depending on whether UnitOfWork has been provided.
            Dictionary<string, object> repositoryCache = (unitOfWork != null ? _repositories : _repositoriesNullUnitOfWork);

            string interfaceShortTypeName = typeof(TRepository).Name;
            if (repositoryCache.ContainsKey(interfaceShortTypeName)) {
                repository = (TRepository) repositoryCache[interfaceShortTypeName];

            } else {
                // Create new, cache it and return it!
                RepositorySettings settings = (RepositorySettings)ConfigurationManager.GetSection(RepositoryMappingConstants.RepositoryMappingsConfigurationSectionName);
                string fullTypeName = settings.RepositoryMapping[interfaceShortTypeName].RepositoryFullTypeName;
                Type t = Type.GetType(fullTypeName);
                repository = Activator.CreateInstance(t, unitOfWork) as TRepository;
                repositoryCache.Add(interfaceShortTypeName, repository);
            }

            return repository;
        }        

        /// <summary>
        /// Creates a Repository without IUnitOfWork implementation! Usually we would like to 
        /// instantiate a Repository <b>with</b> IUnitOfWork.
        /// </summary>
        /// <typeparam name="TRepository"></typeparam>
        /// <typeparam name="TEntity"></typeparam>
        /// <param name="unitOfWork"></param>
        /// <returns></returns>
        public static TRepository GetRepository<TRepository, TEntity>()
            where TRepository : class, IRepository<TEntity>
            where TEntity : EntityBase 
        {
            return GetRepository<TRepository, TEntity>(null);
        }

    } // end public static class RepositoryFactory
}
